// TODO:
// Finish track
// Checkpoints
// Timer/saving stuff
// Overlay with speed, etc.
// Minimap?
// Hazards
// COLLISION DETECTION!!!!
// Make scale reasonable
// Make Track class

#include <cstdlib>
#include <fstream>
#include <iostream>
#include <string>
#include <cmath>
using namespace std;

#include <GL/glut.h>
#include <SOIL.h>

#include "util.h"
#include "Ghostpot.h"
#include "Timer.h"
#include "Tire.h"
#include "Teapot.h"
#include "Track.h"

int updateRate = 17; // milliseconds

// Lighting parameters
GLfloat ambientLight0[] = {0.1f, 0.1f, 0.1f, 1.0f};
GLfloat diffuseLight0[] = {1.0f, 1.0f, 1.0f, 1.0f};
GLfloat specular0[] = {1.0f, 1.0f, 1.0f, 1.0f};
GLfloat spotPosit0[] = {60.0f, 60.0f, 60.0f, 0.0f};
GLfloat spotPointDir0[] = {-60.0f, -60.0f, -60.0f};

const int KEY_ARRAY_SIZE = 256;
int keys[KEY_ARRAY_SIZE];

Teapot pot(0.0, 0.0, 180.0);
Ghostpot gpot(0.0, 0.0, 180.0);
Track track;
Timer timer(3.0, 5.0);

GLuint groundTex;

// Update the offset and frame to create animation
void timerCallback(int value)
{
	glutPostRedisplay();
	glutTimerFunc(updateRate, timerCallback, value);
}

// Draw the scene
void draw()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	// Set proper turning state based on user input
	if (keys[GLUT_KEY_LEFT] && keys[GLUT_KEY_RIGHT] || !keys[GLUT_KEY_LEFT] && !keys[GLUT_KEY_RIGHT])
	{
		pot.turn(Teapot::STRAIGHT);
	}
	else if (keys[GLUT_KEY_LEFT])
	{
		pot.turn(Teapot::LEFT);
	}
	else if (keys[GLUT_KEY_RIGHT])
	{
		pot.turn(Teapot::RIGHT);
	}
	
	// Set proper acceleration state based on user input
	if (keys[GLUT_KEY_UP] && keys[GLUT_KEY_DOWN] || !keys[GLUT_KEY_UP] && !keys[GLUT_KEY_DOWN])
	{
		pot.accelerate(Teapot::COAST);
	}
	else if (keys[GLUT_KEY_UP])
	{
		pot.accelerate(Teapot::FORWARD);
	}
	else if (keys[GLUT_KEY_DOWN])
	{
		pot.accelerate(Teapot::REVERSE);
	} 
	
	// Update teapot speed, position, etc. using new information
	pot.update();
	
	recordGhost();
		
	glPushMatrix();
		

		// Keep the camera behind the teapot
		glTranslatef(-Teapot::STEERING_OFFSET, 0.0, 0.0);
		glRotatef(pot.getHeading() + 180.0, 0.0, 1.0, 0.0);
		glTranslatef(-pot.getX() + Teapot::STEERING_OFFSET, 0.0, -pot.getZ());

		// Set light position
		glLightfv(GL_LIGHT0, GL_POSITION, spotPosit0);
		glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotPointDir0);
		
		// Draw the ground
		/*glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, groundTex);
		glColor3f(1, 1, 1);
		glBegin(GL_QUADS);
			glTexCoord2f(0.0, 0.0); glVertex3f(-1000, 0, -1000);
			glTexCoord2f(0.0, 1.0); glVertex3f(-1000, 0, 1000);
			glTexCoord2f(1.0, 1.0); glVertex3f(1000, 0, 1000);
			glTexCoord2f(1.0, 0.0); glVertex3f(1000, 0, -1000);
		glEnd();*/
		glPushMatrix();
			glScalef(30.0, 30.0, 30.0);
			track.draw();
		glPopMatrix();
		
		// Draw the teapot
		glPushMatrix();
			glTranslatef(0, 5.7, 0);
			pot.draw();
		glPopMatrix();
		
		glPushMatrix();
			glTranslatef(0, 5.7, 0);
			gpot.drawGhost();
		glPopMatrix();

		
		timer.displayTime();
	
	glPopMatrix();

	
	
	
	glFinish();
	glutSwapBuffers();
}

// change the keyState to include the
// direction of the pressed key
void pressArrowKey(int k, int, int)
{
	switch (k)
	{
		case GLUT_KEY_LEFT:
		case GLUT_KEY_RIGHT:
		case GLUT_KEY_UP:
		case GLUT_KEY_DOWN:
			keys[k] = 1;
			break;
	}
}

void releaseArrowKey(int k, int, int)
{
	switch (k)
	{
		case GLUT_KEY_LEFT:
		case GLUT_KEY_RIGHT:
		case GLUT_KEY_UP:
		case GLUT_KEY_DOWN:
			keys[k] = 0;
			break;
	}
}

void key(unsigned char k, int, int)
{
	switch (k)
	{
		case 'Q':
		case 'q':
			saveGhost("ghostrec");
			exit(0);
	}
}

void changeSize (GLsizei w, GLsizei h)
{
	// Setup viewport same size as window minus its borders
	glViewport(0, 0, w, h);

	// change to the PROJECTION matrix, load the Identity so we can
	// setup the matrix for projection into a resized window
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	// pick camera lens
	gluPerspective(45.0, (GLdouble)w / (GLdouble)h, 1.0, 5000.0);

	// aim camera so we can draw stuff later
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	
	// Initial view of the teapot
	gluLookAt(40.0, 20.0, 0.0, 0.0, 6.0, 0.0, 0.0, 1.0, 0.0);
}

void init(int* argc, char* argv[], int width, int height, const string& title)
{
	glutInit(argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(width, height);
	glutInitWindowPosition(0, 0);
	glutCreateWindow(title.c_str());

	glClearColor(0.0, 0.0, 0.0, 1.0);
	
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);
	glShadeModel(GL_SMOOTH);

	glEnable(GL_LIGHTING);
	glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight0);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight0);
	glLightfv(GL_LIGHT0, GL_SPECULAR, specular0);
	glLightfv(GL_LIGHT0, GL_POSITION, spotPosit0);
	glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spotPointDir0);
	glEnable(GL_LIGHT0);

	glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
	glEnable(GL_COLOR_MATERIAL);

	
	glutDisplayFunc(draw);
	glutTimerFunc(updateRate, timerCallback, 0);
	glutReshapeFunc(changeSize);
	glutSpecialFunc(pressArrowKey);
	glutSpecialUpFunc(releaseArrowKey);
	glutKeyboardFunc(key);
}

int main(int argc, char* argv[])
{	
	init(&argc, argv, 800, 450, "Teapot Racing!");
	
	groundTex = loadTex("textures/map.png");
	
	Teapot::setup();
	Track::setup();
	timer.startTimer();
	
	openGhost(gpot, "ghostrec");
	
	pot.setColor(1.0, 0.1, 0.1, 1.0);
	gpot.setColor(0.1, 1.0, 0.1, 1.0);
	
	glutMainLoop();
	
	Teapot::cleanup();
	Track::cleanup();

	return 0;
}
